#include <iostream>
#include <gloox/connectiontcpclient.h>
#include "botsocket.h"
#include "../sockets/SocketHandler.h"
#include "../sockets/ListenSocket.h"
#include "../serversocket.h"
#include <boost/regex.hpp>
#include <fstream>


//Stypedef std :: map<std :: string,bool> services;
//using namespace gloox;

/*
 This class received a data(hosts statuses, process statuses ) from RTMon throw a socket and pass this
 data to  end user throw a jabber acount
 Also this bot can request a agents by himself and pass replies from agents to end user
 */
Bot::Bot() : m_data(0), m_users(0), m_toAll(0), m_status(1), m_eol("aaa"), m_server_config(0)
{
        m_server_config = new CfgParser(1);
        if (m_server_config)
        {
                m_users = m_server_config->getListOfAdmins();
                m_agent_hosts = m_server_config->getAgentHosts();
                m_agent_ips = m_server_config->getAgentIps();
                if (!m_users->empty())
                {
                        std::list <std::string> ::const_iterator iter;
                        for (iter = m_users->begin(); iter != m_users->end(); ++iter)
                                m_admins += "\n" + *iter;
                }
                const gloox::JID jid(*m_server_config->getBotJid());
                m_client = new gloox::Client(jid, *m_server_config->getBotPswd());
                m_client->registerMessageHandler(this);
                m_data = new Data();
                m_data->setBot(this);
        }
        else exit(0);
}

Bot::~Bot()
{
        m_client->disconnect();
        delete (m_client);
        delete m_data;
        delete m_server_config;
}

void Bot::connect()
{
        if (m_client)
                bool result = m_client->connect();
        std::cout << "connection to bot was failed!" << std::endl;
        m_status = 0;
}

void Bot::handleMessage(gloox::Stanza *stanza, gloox::MessageSession* session)
{

        m_sender = stanza->from().full();
        m_sender = m_sender.substr(0, m_sender.find_first_of('/'));
        std::list<std::string> ::const_iterator iter;
        gloox::Stanza *s = 0;
        bool killFlag = false, srvFlag = false, access = false;
        const std::string message = stanza->body();
        //regex: <hostname> kill <processname>
        boost::regex const kill_matcher("^[[:alnum:]]{1,255}\\ kill\\ [[:alnum:]]{1,255}$");
        boost::regex const srv_matcher("^[[:alnum:]]{1,255}\\ srv\\ [[:alnum:]]{1,255}\\ [[:alnum:]-]{4,12}$");
        if (boost::regex_match(message, kill_matcher))
                killFlag = true;
        else if (boost::regex_match(message, srv_matcher))
                srvFlag = true;
        for (iter = m_users->begin(); iter != m_users->end(); ++iter)
                if (*iter == m_sender)
                {

                        access = true;
                        const short message_lenght = message.length();
                        int pos = message.find_first_of(" ");
                        int lpos = message.find_last_of(" ");

                        if (message == "commands")
                                s = gloox::Stanza::createMessageStanza(m_sender,
                                                                       "available commands: *uptime* *hostname* *osver* *osname* *cpu*"
                                                                       " *fs* *load* *mem* *net* *usr* *processes*");
                        else if (message == "info commands")
                                s = gloox::Stanza::createMessageStanza(m_sender,
                                                                       "available commands: *admins* *hosts*");
                        else if (message == "system commands")
                                s = gloox::Stanza::createMessageStanza(m_sender,
                                                                       "available commands: *kill* *srv*");
                        else if (message == "syntax")
                                s = gloox::Stanza::createMessageStanza(m_sender,
                                                                       "\n*routine commands*:\n<hostname> <command>\n*info commands*:\n<command>"
                                                                       "\n*system commands*:\n<hostname> kill <processname>\n<hostname> srv <servicename> <action>");
                        else if (killFlag)
                        {
                                if (!(this->requestWrapper(message.substr(0, pos), "kl" + message.substr(lpos + 1, (message_lenght - (lpos + 1))))))
                                        s = gloox::Stanza::createMessageStanza(m_sender,
                                                                               "host " + message.substr(0, pos) + " unreachable");
                        }
                        else if (srvFlag)
                        {

                                string str = "srv" + message.substr(pos + 5, lpos - pos - 5) + ":"
                                        + message.substr(lpos + 1, (message_lenght - (lpos + 1)));

                                if (!(this->requestWrapper(message.substr(0, pos), str)))
                                        s = gloox::Stanza::createMessageStanza(m_sender,
                                                                               "host " + message.substr(0, pos) + " unreachable");
                        }
                        //routine commands
                        else if (pos != std::string::npos && (pos != 0 || pos != message_lenght - 1))
                        {
                                if (!(this->requestWrapper(message.substr(0, pos), message.substr(pos + 1, (message_lenght - (pos + 1))))))
                                        s = gloox::Stanza::createMessageStanza(m_sender,
                                                                               "host " + message.substr(0, pos) + " unreachable");
                        }
                        else if (message == "hosts")
                        {
                                if (!m_available_hosts.empty())
                                        s = gloox::Stanza::createMessageStanza(m_sender, m_available_hosts);
                                else s = gloox::Stanza::createMessageStanza(m_sender, "The monitor is not accessible!");
                        }

                        else if (message == "admins" && !m_admins.empty())
                                s = gloox::Stanza::createMessageStanza(m_sender, m_admins);
                        else
                                s = gloox::Stanza::createMessageStanza(m_sender,
                                                                       "to help use *commands* *system commands* *info commands* *syntax*");
                        if (s)
                                m_client->send(s);
                        break;

                }
        if (!access && m_sender != *m_server_config->getBotJid())
        {
                s = gloox::Stanza::createMessageStanza(m_sender,
                                                       "you are not authorised to use this service!");
                m_client->send(s);
        }

}

int Bot::SendMsg(const std::string &msg)
{
        std::string message = msg;
        this->replace(message, m_eol, "\n");
        gloox::Stanza *s = 0;
        if (!message.empty() && message == "0x0")
                m_toAll = true;

        else if (!message.empty() && !m_toAll && m_client)
        {

                const gloox::JID recipient(m_sender);
                s = gloox::Stanza::createMessageStanza(recipient, message.c_str());
                m_client->send(s);

        }
        else if (!message.empty() && m_toAll && m_client)
        {
                std::list<std::string> ::const_iterator iter;
                for (iter = m_users->begin(); iter != m_users->end(); ++iter)
                {
                        const gloox::JID recipient(*iter);
                        s = gloox::Stanza::createMessageStanza(recipient, message);
                        m_client->send(s);
                }
                m_toAll = false;
        }
}

void Bot::onConnect()
{

}

bool Bot::onTLSConnect(const gloox::CertInfo& info)
{

}

void Bot::handlePresence(gloox::Stanza *stanza)
{

}

void Bot::listen()
{

        SocketHandler h;
        ListenSocket<BotSocket> l(h, m_data);

        if (l.Bind(11001))
        {
                exit(-1);
        }
        h.Add(&l);
        h.Select(1, 0);
        while (h.GetCount())
        {
                h.Select(1, 0);
        }
}

void Bot::reply(const std::string &msg)
{
        if (!msg.empty())
                SendMsg(msg);
}

bool Bot::request(const std::string &host, const std::string &msg)
{
        SocketHandler h;
        bool flag = false;
        ServerSocket cs(h);
        cs.setReplyHandler(this);
        cs.setCommand(msg);
        cs.Open(host, 11000);
        h.Add(&cs);
        h.Select(1, 0);
        while (h.GetCount())
        {
                h.Select(1, 0);
                flag = true;
        }
        return flag;
}

bool Bot::requestWrapper(const std::string &host, const std::string &msg)
{
        for (uint i = 0; i < m_agent_hosts->size(); i++)
                if (host == m_agent_hosts->at(i))
                        return this->request(m_agent_ips->at(i), msg);
        return 0;
}

//replace substr1 to substr2 in str string

void Bot::replace(std::string &str, const std::string &substr1, const std::string &substr2)
{
        uint pos = str.find(substr1), end = std::string::npos;
        while (pos != end)
        {
                str.replace(pos, substr1.length(), substr2.c_str(), substr2.length());
                pos = str.find(substr1);
        }

}